package dbaccess;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;

import javax.xml.transform.Source;
import javax.xml.ws.Binding;
import javax.xml.ws.Endpoint;
import javax.xml.ws.EndpointReference;

import org.w3c.dom.Element;

import services.Service;
import user.User;

import com.google.gson.Gson;

import dao.DAO;
import dao.DAOFactory;
import dao.EndpointDAO;
import dao.ServiceDAO;
import dao.UserDAO;
import dao.VMDAO;
import entities.Provider;
import entities.Subnet;
import entities.VM;

public class BddManager {
	private BddView bddView;
	private Gson gson;

	/** Instance unique non préinitialisée */
	private static BddManager INSTANCE = null;

	private BddManager() throws ClassNotFoundException, SQLException{
		this.bddView = new BddView();
		this.bddView.connectBdd();
		this.gson = new Gson();
	}

	/** Point d'accès pour l'instance unique à la bdd 
	 * @throws SQLException 
	 * @throws ClassNotFoundException */
	public static BddManager getInstance() throws ClassNotFoundException, SQLException
	{	
		if (INSTANCE == null)
		{ INSTANCE = new BddManager();	
		}
		return INSTANCE;
	}

	/**
	 * Se déconnecter de la base de donnée
	 * @throws SQLException 
	 */
	public void disconnectBdd() throws SQLException{
		this.bddView.disconnectBdd();
	}
	/**
	 * Cree un lien entre 2 users. Par exemple, entre un professeur et un élève.
	 * A potentiellement appeler dans un for ...
	 * @param login1 LOGIN PROF/ADMIN
	 * @param login2 LOGIN ETUDIANT
	 * @throws SQLException
	 */
	public void linkUsers(String login1, String login2) throws SQLException{
		this.bddView.link(login1,login2);
	}

	/**
	 * Retourne, pour un login passé en paramètre, toutes les VM personnes dont le role est role
	 * Par exemple, un prof qui veut recuperer les VM de ses etudiants
	 * @param login PROF OU ADMIN
	 * @param role professeur ou etudiant
	 * @throws Exception 
	 */
	public String getAllVMFromLogin(String loginProfesseur,String role) throws Exception{
		ArrayList<VM> res = new ArrayList<VM>();
		role = role.toLowerCase();
		if(!role.equalsIgnoreCase("professeur") && !role.equalsIgnoreCase("etudiant")){
			throw new Exception("Entrez professeur ou etudiant dans role");
		}else{
			DAO<User> userDAO = DAOFactory.getUserDAO(this.bddView.getConnection());
			ArrayList<String> listLogin = ((UserDAO) userDAO).getLoginEtudiantFromLogin(loginProfesseur,role);
			for(String s : listLogin){
				res.addAll(this.bddView.getVMFromUser(s));
			}
			return gson.toJson(res);
		}
	}

	/**
	 * Retourne une VM depuis l'IDprovider de la VM. Subnet ID a 0 signifie que la VM n'est pas dans un subnet
	 * @param providerID
	 * @param providerName
	 * @return
	 * @throws SQLException
	 */
	public String getVMFromProviderID(int providerID, String providerName) throws SQLException{
		DAO<VM> daoVM = DAOFactory.getVMDAO(this.bddView.getConnection());
		VM vm = ((VMDAO) daoVM).find(providerID,providerName);

		return gson.toJson(vm);
	}


	/**
	 * Update une VM
	 * @param id
	 * @param vmName
	 * @param vmIp
	 * @param osOuTemplate
	 * @param vmRam
	 * @param vmCpu
	 * @param vmHdd
	 * @param nodeName
	 * @param swap
	 * @throws Exception 
	 */
	public void updateVM(int id,String vmName, String vmIp, String osOuTemplate, int vmRam, int vmCpu, int vmHdd, String nodeName,
			int swap, String state, int RightID ) throws Exception{
		if(!state.equalsIgnoreCase("ON") && !state.equalsIgnoreCase("OFF")){
			throw new Exception("Mettez ON ou OFF dans state les bozo");
		}else{
			DAO<VM> daoVM = DAOFactory.getVMDAO(this.bddView.getConnection());
			VM vmToModif = daoVM.find(id);

			//modifier la VM
			vmToModif.update(nodeName, RightID, osOuTemplate, vmHdd, vmRam, swap, vmCpu, vmIp, vmName, state);

			daoVM.update(id, vmToModif);
		}
	}

	/**
	 * Recupere les prof ou les admin d'un user lié
	 * @param userLogin le login de la personne a qui on veut recuperer le user associé
	 * @param role le role de ceux qu'on veut récuperer : professeur OU admin
	 * @return
	 * @throws Exception
	 */
	public String getAdminOrProfFromUser(String userLogin,String role) throws Exception{
		ArrayList<User> res = new ArrayList<User>();
		
		if(!role.equalsIgnoreCase("professeur") && !role.equalsIgnoreCase("admin")){
			throw new Exception("Entrez professeur ou admin dans le role");
		}
		DAO<User> userDAO = DAOFactory.getUserDAO(this.bddView.getConnection());
		ArrayList<String> list = ((UserDAO) userDAO).getLoginAdminOrTeacherFromUser(userLogin, role);
		
		for(String s: list){
			res.add(userDAO.find(s));
		}
		
		return gson.toJson(res);
	}
	
	/**
	 * Retourne toutes les VM de la personne en question
	 * @param loginEtudiant
	 * @return
	 * @throws SQLException
	 */
	public String getAllVMFromUser(String userLogin) throws SQLException{
		ArrayList<VM> res = this.bddView.getVMFromUser(userLogin);
		return this.gson.toJson(res);
	}

	/**
	 * 	
	 * @param login
	 * @param password
	 * @param email
	 * @param provider
	 * @param role Prof ou Etudiant ou Admin
	 * @throws SQLException
	 */
	public void saveUser(String login, String password, String email,String provider,String role) throws Exception{
		if(!role.equalsIgnoreCase("etudiant") &&!role.equalsIgnoreCase("professeur")&&!role.equalsIgnoreCase("admin")){
			throw new Exception("Entrez etudiant OU professeur OU admin dans role");
		}else{
			DAO<User> eleveDao = DAOFactory.getUserDAO(this.bddView.getConnection());
			eleveDao.create(new User(login,password,email,provider,role.toLowerCase()));
		}
	}

	/**
	 * Utile par exemple pour check le password
	 * @param login
	 * @return
	 * @throws SQLException
	 */
	public String getUser(String login) throws SQLException{
		DAO<User> eleveDao = DAOFactory.getUserDAO(this.bddView.getConnection());
		User res = eleveDao.find(login);
		return gson.toJson(res);
	}


	/**
	 * @param login Nom du provider
	 * @param password Password a tester
	 * @throws SQLException 
	 * @returntrue si le password fourni en paramètre est celui correspondant au login
	 */
	public boolean checkPasswordProvider(String login, String password) throws SQLException{
		String passwordFromBdd = this.bddView.getPasswordUser(login,"Provider");
		return password.equals(passwordFromBdd);
	}

	/**
	 * Renvoie la liste des Subnet d'un user
	 * @param login Le login de l'utilisateur
	 * @return Une liste d'objet Subnet, en JSON
	 * @throws SQLException 
	 */
	public String getSubnetFromUser(String login) throws SQLException{
		ArrayList<Subnet> res = this.bddView.getSubnetFromUser(login);
		return this.gson.toJson(res);
	}

	/**
	 * Save un subnet
	 * @param subnetName
	 * @param loginUser
	 * @param numberOfVM
	 * @throws SQLException
	 */
	public void saveSubnet(String subnetName, String loginUser, int numberOfVM) throws SQLException{
		Subnet s = new Subnet(numberOfVM, subnetName, loginUser);
		DAO<Subnet> daoSub = DAOFactory.getSubnetDAO(this.bddView.getConnection());

		daoSub.create(s);
	}

	/**
	 * Retourne l'URL d'un Provider
	 * @return Une liste d'objet Provider, en JSON
	 * @throws SQLException 
	 */
	public String getProviderUrl() throws SQLException{
		ArrayList<Provider> res = this.bddView.getProviderList();
		return this.gson.toJson(res);
	}

	/**
	 * Renvoie la liste des templates d'un provider
	 * @param providerName
	 * @return Une liste de String (nom des templates), en JSON
	 * @throws SQLException 
	 */
	public String getTemplate(String providerName) throws SQLException{
		ArrayList<String> resultat = this.bddView.getTemplate(providerName);
		return this.gson.toJson(resultat);
	}



	/**
	 * Sauvegarde un Provider.
	 * @param name Nom du Provider
	 * @param API_URL L'url pour y acceder
	 * @param listOfTemplate La liste des template qu'il propose
	 * @param userRoles La liste des roles qu'un utilisateur peut avoir
	 * @throws SQLException 
	 */
	public void saveProvider(String name,String email, String API_URL, ArrayList<String> listOfTemplate, String password) throws SQLException{
		this.bddView.getConnection().setAutoCommit(false);

		//ajouter le provider
		this.bddView.addProvider(name,email,API_URL, password);

		//ajouter ses templates
		for(String s : listOfTemplate){
			this.bddView.addTemplate(name,s);
		}

		//ajouter les userRoles
	/*	for(String s : userRoles){
			this.bddView.addRole(name,s);
		}*/
		//ajouter la listeDeService
		//	this.bddView.addService(name, listServices);

		this.bddView.getConnection().commit();
		this.bddView.getConnection().setAutoCommit(true);
	}

	/**
	 * Sauvegarde un endpoint
	 * @param providerName
	 * @param nameEndpoint
	 * @param contentEndpoint
	 * @throws SQLException
	 */
	public void saveEndpoint(String providerName, String nameEndpoint, String contentEndpoint) throws SQLException{
		DAO<entities.Endpoint> daoEndpoint = DAOFactory.getEndpointDAO(this.bddView.getConnection());
		daoEndpoint.create(new entities.Endpoint(nameEndpoint, contentEndpoint, providerName));
	}
	
	/**
	 * Retourne la liste des endpoint
	 * @param providerName
	 * @return
	 * @throws SQLException
	 */
	public String getAllEndpoint(String providerName) throws SQLException{
		DAO<entities.Endpoint> daoEndpoint = DAOFactory.getEndpointDAO(this.bddView.getConnection());
		ArrayList<entities.Endpoint> res = new ArrayList<entities.Endpoint>();
		
		res= ((EndpointDAO) daoEndpoint).findFromProvider(providerName);
		
		return gson.toJson(res);
	}
	
	/**
	 * Sauvegarde une VM.A la sauvegarde la VM possede un etat OFF.
	 * @param login de l'étudiant !!(ou du createur de la VM)
	 * @param vmName
	 * @param vmIp
	 * @param vmTemplate
	 * @throws SQLException 
	 */
	public void saveVM(String loginEtudiant, String vmName, String vmIp, String vmTemplate, int vmProviderID) throws SQLException{
		this.bddView.saveVM( loginEtudiant,  vmName,  vmIp,  vmTemplate, vmProviderID);
	}

	/**
	 * Sauvegarde une VM. A la sauvegarde la VM possede un etat OFF.
	 * @param login
	 * @param vmName
	 * @param vmIp
	 * @param vmOs
	 * @param vmRam
	 * @param vmCpu
	 * @param vmHdd
	 * @param nodeName
	 * @param swap
	 * @throws SQLException 
	 */
	public void saveVM(String login, String vmName, String vmIp, String vmOs, int vmRam, int vmCpu, int vmHdd, String nodeName,
			int swap, int vmProviderID) throws SQLException{
		this.bddView.saveVM( login,  vmName,  vmIp,  vmOs,  vmRam,  vmCpu,  vmHdd,  nodeName, swap, vmProviderID);
	}

	/**
	 * Supprime une VM d'un utilisateur, si cette derniere lui appartient. Sinon ne fait rien.
	 * @param login
	 * @param vmID ID de la VM (notre ID)
	 * @throws SQLException 
	 */
	public void deleteVM(int vmID) throws SQLException{
		DAO<VM> vmDAO = DAOFactory.getVMDAO(this.bddView.getConnection());
		vmDAO.delete(vmID);
	}

	/**
	 * Add a VM in a Subnet. Si les ID sont faux, rien ne se passe.
	 * @param vmID
	 * @param subnetID
	 * @throws SQLException
	 */
	public void addVMInSubnet(int vmID, int subnetID) throws SQLException{
		this.bddView.addVMInSubnet(vmID,subnetID);
	}

	/**
	 * Affecte l'état d'une VM (allumée ou éteinte). METTRE seulement "ON" ou "OFF" dans le champ state..
	 * @param vmID
	 * @param state "ON" ou "OFF"
	 * @throws SQLException
	 */
	public void setVmState(int vmID,String state)throws SQLException{
		if(!(state.equalsIgnoreCase("ON") || state.equalsIgnoreCase("OFF"))){
			System.err.println("METTRE ON OU OFF DANS LE CHAMPS STATE dans la fonction setVMstate. Merci de votre compréhension");
		}else{
			this.bddView.setVmState(vmID,state);
		}
	}

	/**
	 * Retourne l'état courant de la VM. Lorsqu'une VM vient d'etre cree sont etat est OFF
	 * @param vmid
	 * @return l'etat de la VM
	 * @throws SQLException
	 */
	public String getVmState(int vmid)throws SQLException{
		return this.bddView.getVmState(vmid);
	}

	/**
	 * Ajoute un service dans la base de donnée. Mettre les listes a null si vides
	 *
	 * @param name
	 * @param networkSelected
	 * @param autoTemplateSelected
	 * @param authenticationSelected
	 * @param ipAdressSelected
	 * @param manualTemplateSelected
	 * @param listOfTemplate
	 * @param osSelected
	 * @param listOfOs
	 * @param minHdd
	 * @param maxHdd
	 * @param HddSelected
	 * @param minRam
	 * @param maxRam
	 * @param RamSelected
	 * @param minSwap
	 * @param maxSwap
	 * @param swapSelected
	 * @param minCpu
	 * @param maxCpu
	 * @param cpuSelected
	 * @throws SQLException
	 */
	public void addService(String name, String networkSelected,
			String autoTemplateSelected,String authenticationSelected,String ipAdressSelected,
			String manualTemplateSelected, ArrayList<String> listOfTemplate,
			String osSelected, ArrayList<String> listOfOs,
			String minHdd, String maxHdd, String HddSelected,
			String minRam,String maxRam, String RamSelected,
			String minSwap, String maxSwap, String swapSelected,
			String minCpu, String maxCpu, String cpuSelected, String provider) throws SQLException {
		//creer le service
		Service s = new Service();

		s.fillString(name, networkSelected, autoTemplateSelected, authenticationSelected, ipAdressSelected, provider);
		s.fillSelectedList(osSelected, listOfOs, manualTemplateSelected, listOfTemplate);
		s.fillMinMax(minHdd, maxHdd, HddSelected, minRam, maxRam, RamSelected, minSwap, maxSwap, swapSelected, minCpu, maxCpu, cpuSelected);

		DAO<Service> serviceDao = DAOFactory.getServiceDAO(this.bddView.getConnection());

		serviceDao.create(s);

	}

	/**
	 * Renvoie le service dont le nom est passe en parametre
	 * @param serviceName nom du service
	 * @return le servicea
	 * @throws SQLException
	 */
	private Service getService (String serviceName) throws SQLException{
		Service s = new Service();

		DAO<Service> serviDao = DAOFactory.getServiceDAO(this.bddView.getConnection());
		s = serviDao.find(serviceName); 
		return s;
	}

	/**
	 * Retourne tous les services d'un provider
	 * @param providerName
	 * @return
	 * @throws SQLException
	 */
	public String getAllServiceFromProvider(String providerName) throws SQLException{
		DAO<Service> serviDao = DAOFactory.getServiceDAO(this.bddView.getConnection());
		ArrayList<Service> res = new ArrayList<Service>();

		//recupere la liste de tous les noms des services pour un provider
		ArrayList<String> resultat =((ServiceDAO) serviDao).getNameServiceFromProvider(providerName);

		//on recupere ensuite tous les services
		for(String s:resultat){
			res.add(this.getService(s));
		}
		return gson.toJson(res);
	}

	/**
	 * Retourne tous les services présents dans la base de donnée
	 * @return
	 * @throws SQLException
	 */
	public String getAllService() throws SQLException{
		DAO<Service> serviDao = DAOFactory.getServiceDAO(this.bddView.getConnection());
		ArrayList<Service> res = new ArrayList<Service>();

		//recupere la liste de tous les noms des services pour un provider
		ArrayList<String> resultat =((ServiceDAO) serviDao).getAllServiceName();

		//on recupere ensuite tous les services
		for(String s:resultat){
			res.add(this.getService(s));
		}
		return gson.toJson(res);
	}

	/**
	 * Renvoie le mail d'un provider
	 * @param providerName
	 * @return son mail
	 * @throws SQLException
	 */
	public String getProviderMail(String providerName) throws SQLException{
		return this.bddView.getProviderMail(providerName);
	}

	/**
	 * Retourne un provider dont le nom est passé en paramètre
	 * @param name
	 * @return
	 * @throws SQLException
	 */
	public String getProvider(String name) throws SQLException{
		DAO<Provider> daoProvider = DAOFactory.getProviderDAO(this.bddView.getConnection());
		Provider p = daoProvider.find(name);
		return gson.toJson(p);
	}
}
